package com.dcy.log.aspect;

import cn.dev33.satoken.spring.SpringMVCUtil;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.dcy.common.annotation.Log;
import com.dcy.common.constant.SkyWalkingConstant;
import com.dcy.log.constant.LogStatusEnum;
import com.dcy.log.model.OperationalLog;
import com.dcy.log.properties.LogProperties;
import com.dcy.satoken.util.AdminLoginUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.skywalking.apm.toolkit.trace.ActiveSpan;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Optional;

/**
 * @Author：dcy
 * @Description: 日志aop
 * @Date: 2019/10/15 9:05
 */
@Aspect
@Slf4j(topic = "request-log")
@Order(1)
public class WebLogAspect {
    @Autowired
    private LogProperties logProperties;

    /**
     * ..表示包及子包 该方法代表controller层的所有方法
     * Pointcut定义时，还可以使用&&、||、! 这三个运算
     */
    @Pointcut("@annotation(com.dcy.common.annotation.Log)")
    public void logMethod() {
    }

    /**
     * 后置通知
     */
    @AfterReturning(pointcut = "logMethod()", returning = "result")
    public void logResultVoInfo(JoinPoint joinPoint, Object result) {
        handleLog(joinPoint, null, result);
    }

    /**
     * 异常通知
     *
     * @param joinPoint
     * @param exception
     */
    @AfterThrowing(value = "logMethod()", throwing = "exception")
    public void doAfterThrowingAdvice(JoinPoint joinPoint, Exception exception) {
        handleLog(joinPoint, exception, null);
    }

    /**
     * 处理log
     *
     * @param joinPoint
     * @param exception
     * @param result
     */
    private void handleLog(final JoinPoint joinPoint, final Exception exception, Object result) {
        // 增加自定义标签
        Optional.ofNullable(AdminLoginUtil.getUserInfo()).ifPresent(loginAccount -> {
            ActiveSpan.tag(SkyWalkingConstant.USER_ID, loginAccount.getId());
            ActiveSpan.tag(SkyWalkingConstant.USER_NAME, loginAccount.getUsername());
        });
        if (!SpringMVCUtil.isWeb() || !logProperties.getEnable()) {
            return;
        }
        final HttpServletRequest request = SpringMVCUtil.getRequest();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        OperationalLog operationalLog = new OperationalLog();
        Log logAnnotation = AnnotationUtil.getAnnotation(methodSignature.getMethod(), Log.class);
        operationalLog.setLogEnum(logAnnotation.type());
        operationalLog.setIp(ServletUtil.getClientIP(request));
        operationalLog.setUrl(URLUtil.getPath(request.getRequestURI()));
        operationalLog.setMethod(methodSignature.getDeclaringTypeName() + "." + methodSignature.getName());
        operationalLog.setOperName(AdminLoginUtil.getUserName());
        operationalLog.setCreateDate(new Date());
        operationalLog.setLogStatus(LogStatusEnum.NORMAL.getCode());
        operationalLog.setOperParam(HttpUtil.toParams(request.getParameterMap()));
        setBusinessName(methodSignature, operationalLog);
        if (exception != null) {
            operationalLog.setLogStatus(LogStatusEnum.ABNORMAL.getCode());
            operationalLog.setError(exception.getMessage());
        }
        if (result != null) {
            operationalLog.setResult(JSON.toJSONString(result));
        }
        // 打印日志
        printLog(operationalLog);
    }

    /**
     * 打印日志
     *
     * @param operationalLog
     */
    private void printLog(OperationalLog operationalLog) {
        if (StrUtil.isNotBlank(operationalLog.getError())) {
            log.error(JSON.toJSONString(operationalLog));
        } else {
            log.info(JSON.toJSONString(operationalLog));
        }
    }


    private static void setBusinessName(MethodSignature methodSignature, OperationalLog operationalLog) {
        String businessName = StrUtil.EMPTY;
        // 类注解
        Api api = AnnotationUtil.getAnnotation(methodSignature.getDeclaringType(), Api.class);
        // 方法注解
        ApiOperation annotation = AnnotationUtil.getAnnotation(methodSignature.getMethod(), ApiOperation.class);
        if (api.tags().length > 0) {
            businessName += api.tags()[0];
        }
        if (annotation != null) {
            if (StrUtil.isNotBlank(businessName)) {
                businessName = businessName + ":" + annotation.value();
            } else {
                businessName += annotation.value();
            }
        }
        operationalLog.setBusinessName(businessName);
    }
}
